﻿//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Collections.Generic;
using EffiProzDB.Store;



namespace EffiProzDB.Lib
{
    /**
     * This class does not store null keys.
     *
     * @author fredt@users
     * @version 1.7.2
     * @since 1.7.2
     */
    public class HashMappedList<TKey, TValue> : BaseHashMap<TKey, TValue>
    {

        public HashMappedList()
            : this(16, 0.75f)
        {

        }

        public HashMappedList(int initialCapacity)
            : this(initialCapacity, 0.75f)
        {

        }

        public HashMappedList(int initialCapacity,
                       float loadFactor)
            : base(initialCapacity, loadFactor, false)
        {

        }

        public new void Clear()
        {
            base.Clear();
        }

        public TValue this[TKey key]
        {
            get
            {
                int hash = key.GetHashCode();
                int lookup = getLookup(key, hash);
                if (lookup != -1)
                {
                    return objectValueTable[lookup];
                }
                else
                    throw new InvalidOperationException();
            }

            set
            {
                AddOrRemove(key, value, false);
            }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            int hash = key.GetHashCode();
            int lookup = getLookup(key, hash);
            if (lookup != -1)
            {
                value = objectValueTable[lookup];
                return true;
            }
            else
            {
                value = default(TValue);
                return false;
            }
        }


        public void Add(TKey key, TValue value)
        {
            if (key == null)
            {
                throw new ArgumentNullException();
            }
            else if (ContainsKey(key))
            {
                throw new ArgumentException();
            }

            AddOrRemove(key, value, false);
        }

        public bool Remove(TKey key)
        {
            if (key == null)
            {
                throw new ArgumentNullException();
            }

            int lookup = getLookup(key, key.GetHashCode());
            if (lookup < 0)
            {
                //throw new ArgumentException();
                return false; // SortedList semanics
            }

            bool result = base.RemoveObject(key);
            RemoveRow(lookup);

            return result;
        }

        public bool RemoveAt(int index)
        {
            CheckRange(index);
            return Remove(objectKeyTable[index]);
        }

        public new bool ContainsKey(TKey key)
        {
            return base.ContainsKey(key);
        }

        public new bool ContainsValue(TValue value)
        {
            return base.ContainsValue(value);
        }

        public void putAll(HashMappedList<TKey, TValue> t)
        {
            foreach (var key in t.Keys)
            {
                Add(key, t[key]);
            }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return new HashMappedListEnum(this);
        }


        public IList<TKey> Keys
        {
            get
            {
                return new KeyList(this);
            }
        }

        public IList<TValue> Values
        {
            get
            {
                return new ValueList(this);
            }
        }

        private void CheckRange(int i)
        {

            if (i < 0 || i >= Count)
            {
                throw new ArgumentOutOfRangeException();
            }
        }

        public bool setValue(int index, TKey key, TValue value)
        {
            CheckRange(index);

            if (Keys.Contains(key) && getLookup(key, key.GetHashCode()) != index)
            {
                return false;
            }

            // Remove(objectKeyTable[index]);
            AddOrRemove(key, value, false);

            return true;
        }

        public bool setKey(int index, TKey key, TValue value)
        {
            CheckRange(index);

            if (Keys.Contains(key))
            {
                throw new ArgumentException("Key already exists");
            }

            TKey oldKey = objectKeyTable[index];

            AddOrRemove(oldKey, value, true);

            AddOrRemove(key, value, false);

            return true;
        }


        public class HashMappedListEnum : IEnumerator<KeyValuePair<TKey, TValue>>
        {

            int lookup = -1;
            int counter = 0;
            bool removed = false;
            HashMappedList<TKey, TValue> _o = null;
            /**
             * default is iterator for values
             */
            public HashMappedListEnum(HashMappedList<TKey, TValue> _o) { this._o = _o; }


            public bool MoveNext()
            {
                if (!(counter < _o.Count))
                    return false;

                removed = false;
                counter++;
                lookup = _o.NextLookup(lookup);

                return true;
            }

            public void Reset()
            {
                lookup = -1;
                counter = 0;
            }

            public KeyValuePair<TKey, TValue> Current
            {
                get
                {
                    KeyValuePair<TKey, TValue> pair = new KeyValuePair<TKey, TValue>
                        (_o.objectKeyTable[lookup], _o.objectValueTable[lookup]);

                    return pair;
                }

            }

            object System.Collections.IEnumerator.Current
            {
                get
                {
                    KeyValuePair<TKey, TValue> pair = new KeyValuePair<TKey, TValue>
                        (_o.objectKeyTable[lookup], _o.objectValueTable[lookup]);

                    return pair;
                }

            }

            public void Remove()
            {

                if (removed)
                {
                    throw new InvalidOperationException();
                }

                counter--;
                removed = true;
                _o.RemoveLookup(lookup);
            }

            public int GetAccessCount()
            {

                if (removed || _o.accessTable == null)
                {
                    throw new InvalidOperationException("");
                }

                return _o.accessTable[lookup];
            }

            public void Dispose()
            {
            }

        }


        public class KeyList : IList<TKey>
        {

            HashMappedList<TKey, TValue> _o;

            public KeyList(HashMappedList<TKey, TValue> _o)
            {
                this._o = _o;

            }

            public int Count
            {
                get
                {
                    return _o.Count;
                }
            }

            public bool IsReadOnly
            {
                get
                {
                    return false;
                }
            }

            public void Add(TKey key)
            {
                throw new NotSupportedException();
            }

            public void Clear()
            {
                throw new NotSupportedException();
            }


            public bool Contains(TKey o)
            {
                return _o.ContainsKey(o);
            }

            public void CopyTo(TKey[] array, int arrayIndex)
            {
                throw new NotSupportedException();
            }

            public TKey this[int index]
            {
                get
                {
                    _o.CheckRange(index);
                    return _o.objectKeyTable[index];
                }
                set
                {
                    _o.CheckRange(index);
                    TValue val = _o.objectValueTable[index];
                    _o.setKey(index, value, val);
                }
            }

            public IEnumerator<TKey> GetEnumerator()
            {
                return new BaseHashKeyEnum(_o);
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return new BaseHashKeyEnum(_o);
            }

            public int IndexOf(TKey key)
            {
                return _o.getLookup(key, key.GetHashCode());
            }

            public void Insert(int index, TKey item)
            {
                throw new NotSupportedException();
            }


            public bool Remove(TKey key)
            {
                throw new NotSupportedException();
            }

            public void RemoveAt(int index)
            {
                throw new NotSupportedException();
            }


        }

        public class ValueList : IList<TValue>
        {

            HashMappedList<TKey, TValue> _o;

            public ValueList(HashMappedList<TKey, TValue> _o)
            {
                this._o = _o;

            }

            public int Count
            {
                get
                {
                    return _o.Count;
                }
            }

            public bool IsReadOnly
            {
                get
                {
                    return false;
                }
            }

            public void Add(TValue value)
            {
                throw new NotSupportedException();
            }

            public void Clear()
            {
                throw new NotSupportedException();
            }


            public bool Contains(TValue value)
            {
                return _o.ContainsValue(value);
            }

            public void CopyTo(TValue[] array, int arrayIndex)
            {
                throw new NotSupportedException();
            }

            public TValue this[int index]
            {
                get
                {
                    _o.CheckRange(index);
                    return _o.objectValueTable[index];
                }
                set
                {
                    _o.CheckRange(index);
                    TKey key = _o.objectKeyTable[index];
                    _o.setValue(index, key, value);
                }
            }

            public IEnumerator<TValue> GetEnumerator()
            {
                return new BaseHashValueEnum(_o);
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return new BaseHashValueEnum(_o);
            }

            public int IndexOf(TValue value)
            {
                throw new NotSupportedException();
            }

            public void Insert(int index, TValue item)
            {
                throw new NotSupportedException();
            }


            public bool Remove(TValue key)
            {
                throw new NotSupportedException();
            }

            public void RemoveAt(int index)
            {
                throw new NotSupportedException();
            }
        }

    }


    public class BaseHashMap<TKey, TValue>
    {

        /**
         * Base class for hash tables or sets. The exact type of the structure is
         * defined by the constructor. Each instance has at least a keyTable array
         * and a HashIndex instance for looking up the keys into this table. Instances
         * that are maps also have a valueTable the same size as the keyTable.
         *
         * Special getOrAddXXX() methods are used for object maps _in some subclasses.
         *
         * @author fredt@users
         * @version 1.8.0
         * @since 1.7.2
         */
        /*

            data store:
            keys: {array of primitive | array of object}
            values: {none | array of primitive | array of object} same size as keys
            objects support : hashCode(), equals()

            implemented types of keyTable:
            {objectKeyTable: variable size Object[] array for keys |
            intKeyTable: variable size int[] for keys |
            longKeyTable: variable size long[] for keys }

            implemented types of valueTable:
            {objectValueTable: variable size Object[] array for values |
            intValueTable: variable size int[] for values |
            longValueTable: variable size long[] for values}

            valueTable does not exist for sets or for object pools

            hash index:
            hashTable: fixed size int[] array for hash lookup into keyTable
            linkTable: pointer to the next key ; size equal or larger than hashTable
            but equal to the valueTable

            access count table:
            {none |
            variable size int[] array for access count} same size as xxxKeyTable
        */


        //
        protected HashIndex hashIndex;

        //

        protected TKey[] objectKeyTable;

        //
        protected TValue[] objectValueTable;

        //
        protected int accessMin;
        protected int accessCount;
        protected int[] accessTable;

        //
        protected float loadFactor;
        protected int initialCapacity;
        protected int threshold;
        protected int maxCapacity;
        protected int purgePolicy = NO_PURGE;
        protected bool minimizeOnEmpty;

        //
        bool hasZeroKey;
        int zeroKeyIndex = -1;



        // purgePolicy
        public const int NO_PURGE = 0;
        public const int PURGE_ALL = 1;
        public const int PURGE_HALF = 2;
        public const int PURGE_QUARTER = 3;

        protected BaseHashMap(int initialCapacity, float loadFactor,
                              bool hasAccessCount)
        {

            this.loadFactor = loadFactor;
            this.initialCapacity = initialCapacity;

            if (initialCapacity <= 0 || loadFactor <= 0.0)
            {
                throw new ArgumentException();
            }

            threshold = initialCapacity;

            if (threshold < 3)
            {
                threshold = 3;
            }

            int hashtablesize = (int)(initialCapacity * loadFactor);

            if (hashtablesize < 3)
            {
                hashtablesize = 3;
            }

            hashIndex = new HashIndex(hashtablesize, initialCapacity, true);

            int arraySize = threshold;

            objectKeyTable = new TKey[arraySize];

            objectValueTable = new TValue[arraySize];


            if (hasAccessCount)
            {
                accessTable = new int[arraySize];
            }
        }

        protected int getLookup(TKey key, int hash)
        {

            int lookup = hashIndex.getLookup(hash);
            TKey tempKey;

            for (; lookup >= 0; lookup = hashIndex.getNextLookup(lookup))
            {
                tempKey = objectKeyTable[lookup];

                if (key.Equals(tempKey))
                {
                    return lookup;
                }
            }

            return lookup;
        }




        /**
         * generic method for adding or removing keys
         */
        protected TValue AddOrRemove(TKey objectKey, TValue objectValue,
                                     bool remove)
        {

            if (objectKey == null)
            {
                return default(TValue);
            }
            int hash = objectKey.GetHashCode();

            int index = hashIndex.getHashIndex(hash);
            int lookup = hashIndex.hashTable[index];
            int lastLookup = -1;
            TValue returnValue = default(TValue);

            for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex.getNextLookup(lookup))
            {

                if (objectKeyTable[lookup].Equals(objectKey))
                {
                    break;
                }
            }

            if (lookup >= 0)
            {
                if (remove)
                {

                    objectKeyTable[lookup] = default(TKey);


                    returnValue = objectValueTable[lookup];
                    objectValueTable[lookup] = default(TValue);

                    hashIndex.unlinkNode(index, lastLookup, lookup);

                    if (accessTable != null)
                    {
                        accessTable[lookup] = 0;
                    }

                    if (minimizeOnEmpty && hashIndex.elementCount == 0)
                    {
                        Rehash(initialCapacity);
                    }

                    return returnValue;
                }


                returnValue = objectValueTable[lookup];
                objectValueTable[lookup] = objectValue;


                if (accessTable != null)
                {
                    accessTable[lookup] = accessCount++;
                }

                return returnValue;
            }

            // not found
            if (remove)
            {
                return default(TValue);
            }

            if (hashIndex.elementCount >= threshold)
            {

                // should throw maybe, if reset returns false?
                if (Reset())
                {
                    return AddOrRemove(objectKey, objectValue, remove);
                }
                else
                {
                    return default(TValue);
                }
            }

            lookup = hashIndex.linkNode(index, lastLookup);

            // type dependent block

            objectKeyTable[lookup] = objectKey;

            objectValueTable[lookup] = objectValue;


            //
            if (accessTable != null)
            {
                accessTable[lookup] = accessCount++;
            }

            return returnValue;
        }

        /**
         * type specific method for Object sets or Object->Object maps
         */
        protected bool RemoveObject(TKey objectKey)
        {

            if (objectKey == null)
            {
                throw new ArgumentNullException();
            }

            int hash = objectKey.GetHashCode();
            int index = hashIndex.getHashIndex(hash);
            int lookup = hashIndex.hashTable[index];
            int lastLookup = -1;
            Object returnValue = null;

            for (; lookup >= 0;
                    lastLookup = lookup,
                    lookup = hashIndex.getNextLookup(lookup))
            {
                if (objectKeyTable[lookup].Equals(objectKey))
                {
                    objectKeyTable[lookup] = default(TKey);

                    hashIndex.unlinkNode(index, lastLookup, lookup);


                    returnValue = objectValueTable[lookup];
                    objectValueTable[lookup] = default(TValue);

                    return true;
                }
            }

            // not found
            return false;
        }

        protected bool Reset()
        {

            if (maxCapacity == 0 || maxCapacity > threshold)
            {
                Rehash(hashIndex.hashTable.Length * 2);

                return true;
            }
            else if (purgePolicy == PURGE_ALL)
            {
                Clear();

                return true;
            }
            else if (purgePolicy == PURGE_QUARTER)
            {
                Clear(threshold / 4, threshold >> 8);

                return true;
            }
            else if (purgePolicy == PURGE_HALF)
            {
                Clear(threshold / 2, threshold >> 8);

                return true;
            }
            else if (purgePolicy == NO_PURGE)
            {
                return false;
            }

            return false;
        }

        /**
         * rehash uses existing key and element arrays. key / value pairs are
         * put back into the arrays from the top, removing any gaps. any redundant
         * key / value pairs duplicated at the end of the array are then cleared.
         *
         * newCapacity must be larger or equal to existing number of elements.
         */
        protected void Rehash(int newCapacity)
        {

            int limitLookup = hashIndex.newNodePointer;
            bool oldZeroKey = hasZeroKey;
            int oldZeroKeyIndex = zeroKeyIndex;

            if (newCapacity < hashIndex.elementCount)
            {
                return;
            }

            hashIndex.reset((int)(newCapacity * loadFactor), newCapacity);

            hasZeroKey = false;
            zeroKeyIndex = -1;
            threshold = newCapacity;

            for (int lookup = -1;
                    (lookup = NextLookup(lookup, limitLookup, oldZeroKey, oldZeroKeyIndex))
                    < limitLookup; )
            {

                TKey objectKey = default(TKey);
                TValue objectValue = default(TValue);

                objectKey = objectKeyTable[lookup];

                objectValue = objectValueTable[lookup];


                AddOrRemove(objectKey, objectValue, false);

                if (accessTable != null)
                {
                    accessTable[hashIndex.elementCount - 1] = accessTable[lookup];
                }
            }

            ResizeElementArrays(hashIndex.newNodePointer, newCapacity);
        }

        /**
         * resize the arrays contianing the key / value data
         */
        private void ResizeElementArrays(int dataLength, int newLength)
        {

            Array temp;
            int usedLength = newLength > dataLength ? dataLength
                                                      : newLength;

            temp = objectKeyTable;
            objectKeyTable = new TKey[newLength];
            Array.Copy((Array)temp, 0, objectKeyTable, 0, usedLength);


            temp = objectValueTable;
            objectValueTable = new TValue[newLength];
            Array.Copy((Array)temp, 0, objectValueTable, 0, usedLength);


            if (accessTable != null)
            {
                temp = accessTable;
                accessTable = new int[newLength];

                Array.Copy((Array)temp, 0, accessTable, 0, usedLength);
            }
        }

        /**
         * clear all the key / value data _in a range.
         */
        private void ClearElementArrays(int from, int to)
        {

            int counter = to;
            while (--counter >= from)
            {
                objectKeyTable[counter] = default(TKey);
            }

            counter = to;
            while (--counter >= from)
            {
                objectValueTable[counter] = default(TValue);
            }

            if (accessTable != null)
            {
                counter = to;

                while (--counter >= from)
                {
                    accessTable[counter] = 0;
                }
            }
        }

        /**
         * move the elements after a removed key / value pair to fill the gap
         */
        void RemoveFromElementArrays(int lookup)
        {
            int arrayLength = hashIndex.linkTable.Length;

            Array array = objectKeyTable;
            Array.Copy(array, lookup + 1, array, lookup,
                             arrayLength - lookup - 1);
            objectKeyTable[arrayLength - 1] = default(TKey);


            array = objectValueTable;
            Array.Copy(array, lookup + 1, array, lookup,
                             arrayLength - lookup - 1);
            objectValueTable[arrayLength - 1] = default(TValue);
        }

        /**
         * find the next lookup _in the key/value tables with an entry
         * allows the use of old limit and zero int key attributes
         */
        int NextLookup(int lookup, int limitLookup, bool hasZeroKey,
                       int zeroKeyIndex)
        {

            for (++lookup; lookup < limitLookup; lookup++)
            {
                if (objectKeyTable[lookup] != null)
                {
                    return lookup;
                }
            }

            return lookup;
        }

        /**
         * find the next lookup _in the key/value tables with an entry
         * uses current limits and zero integer key state
         */
        protected int NextLookup(int lookup)
        {

            for (++lookup; lookup < hashIndex.newNodePointer; lookup++)
            {
                if (objectKeyTable[lookup] != null)
                {
                    return lookup;
                }
            }

            return lookup;
        }

        /**
         * row must already been freed of key / element
         */
        protected void RemoveRow(int lookup)
        {
            hashIndex.removeEmptyNode(lookup);
            RemoveFromElementArrays(lookup);
        }

        protected TValue RemoveLookup(int lookup)
        {

            return AddOrRemove(objectKeyTable[lookup], default(TValue), true);

        }

        /**
         * Clear the map completely.
         */
        public void Clear()
        {

            accessCount = 0;
            accessMin = accessCount;
            hasZeroKey = false;
            zeroKeyIndex = -1;

            ClearElementArrays(0, hashIndex.linkTable.Length);
            hashIndex.clear();

            if (minimizeOnEmpty)
            {
                Rehash(initialCapacity);
            }
        }

        /**
         * Return the max accessCount value for count elements with the lowest
         * access count. Always return at least accessMin + 1
         */
        protected int GetAccessCountCeiling(int count, int margin)
        {
            return ArrayCounter.rank(accessTable, hashIndex.newNodePointer, count,
                                     accessMin + 1, accessCount, margin);
        }

        /**
         * Clear approximately count elements from the map, starting with
         * those with low accessTable ranking.
         *
         * Only for maps with Object key table
         */
        protected void Clear(int count, int margin)
        {

            if (margin < 64)
            {
                margin = 64;
            }

            int maxlookup = hashIndex.newNodePointer;
            int accessBase = GetAccessCountCeiling(count, margin);

            for (int lookup = 0; lookup < maxlookup; lookup++)
            {
                TKey o = objectKeyTable[lookup];

                if (o != null && accessTable[lookup] < accessBase)
                {
                    RemoveObject(o);
                }
            }

            accessMin = accessBase;
        }

        protected void ResetAccessCount()
        {

            if (accessCount < int.MaxValue)
            {
                return;
            }

            accessMin >>= 2;
            accessCount >>= 2;

            int i = accessTable.Length;

            while (--i >= 0)
            {
                accessTable[i] >>= 2;
            }
        }

        public int Count
        {
            get
            {
                return hashIndex.elementCount;
            }
        }

        public bool IsEmpty()
        {
            return hashIndex.elementCount == 0;
        }

        protected bool ContainsKey(TKey key)
        {

            if (key == null)
            {
                return false;
            }

            int lookup = getLookup(key, key.GetHashCode());

            return lookup == -1 ? false : true;
        }



        protected bool ContainsValue(TValue value)
        {

            int lookup = 0;

            if (value == null)
            {
                for (; lookup < hashIndex.newNodePointer; lookup++)
                {
                    if (objectValueTable[lookup] == null)
                    {
                        if (objectKeyTable[lookup] != null)
                        {
                            return true;
                        }
                    }
                }
            }
            else
            {
                for (; lookup < hashIndex.newNodePointer; lookup++)
                {
                    if (value.Equals(objectValueTable[lookup]))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /**
         * Iterator returns Object, int or long and is used both for keys and
         * values
         */
        public class BaseHashKeyEnum : IEnumerator<TKey>
        {
            int lookup = -1;
            int counter;
            bool removed;
            BaseHashMap<TKey, TValue> _o;
            /**
             * default is iterator for values
             */
            public BaseHashKeyEnum(BaseHashMap<TKey, TValue> _o) { this._o = _o; }


            public bool MoveNext()
            {
                if (!(counter < _o.hashIndex.elementCount))
                    return false;

                removed = false;
                counter++;
                lookup = _o.NextLookup(lookup);

                return true;
            }

            public void Reset()
            {
                lookup = -1;
                counter = 0;
            }

            public TKey Current
            {
                get
                {
                    return _o.objectKeyTable[lookup];
                }

            }

            object System.Collections.IEnumerator.Current
            {
                get
                {
                    return _o.objectKeyTable[lookup];
                }

            }

            public void Remove()
            {

                if (removed)
                {
                    throw new InvalidOperationException("Hash Iterator");
                }

                counter--;
                removed = true;
                _o.RemoveLookup(lookup);

            }

            public int GetAccessCount()
            {

                if (removed || _o.accessTable == null)
                {
                    throw new InvalidOperationException("");
                }

                return _o.accessTable[lookup];
            }

            public void Dispose()
            {
            }

        }

        public class BaseHashValueEnum : IEnumerator<TValue>
        {
            int lookup = -1;
            int counter;
            bool removed;
            BaseHashMap<TKey, TValue> _o;
            /**
             * default is iterator for values
             */
            public BaseHashValueEnum(BaseHashMap<TKey, TValue> _o) { this._o = _o; }


            public bool MoveNext()
            {
                if (!(counter < _o.hashIndex.elementCount))
                    return false;

                removed = false;
                counter++;
                lookup = _o.NextLookup(lookup);

                return true;
            }

            public void Reset()
            {
                lookup = -1;
                counter = 0;
            }

            public TValue Current
            {
                get
                {
                    return _o.objectValueTable[lookup];
                }

            }

            object System.Collections.IEnumerator.Current
            {
                get
                {
                    return _o.objectValueTable[lookup];
                }

            }

            public void Remove()
            {

                if (removed)
                {
                    throw new InvalidOperationException("Hash Iterator");
                }

                counter--;
                removed = true;
                _o.RemoveLookup(lookup);

            }

            public int GetAccessCount()
            {

                if (removed || _o.accessTable == null)
                {
                    throw new InvalidOperationException("");
                }

                return _o.accessTable[lookup];
            }

            public void Dispose()
            {
            }

        }

    }
}


